//
//  ChangeCheckerAppDelegate.m
//  iChangeChecker
//
//  Created by Lauri Koskela on 16.4.2011.
//  Copyright 2011 Lauri Koskela. All rights reserved.
//

#import "iChangeCheckerAppDelegate.h"
#import "SiteArrayController.h"
#import "UpdatedPageMenuItem.h"
#import "UpdateOperation.h"
#import <CommonCrypto/CommonDigest.h>
#import <OAuthConsumer/OAToken_KeychainExtensions.h>

#define kGeneralTab @"GeneralPrefTab"
#define kPagesTab @"PagesPrefTab"

@implementation ChangeCheckerAppDelegate

@synthesize toolbar;
@synthesize preferencesPagesView;
@synthesize preferencesGeneralView;
@synthesize mainView;
@synthesize generalItem;
@synthesize pagesItem;
@synthesize window;
@synthesize updateIntervalPopUp;
@synthesize siteTableView;
@synthesize loginStartCheckBox;
@synthesize statusMenu;
@synthesize siteArrayController;
@synthesize titleField;
@synthesize addressField;
@synthesize statusItem;
@synthesize separator;
@synthesize updatedLabel;
@synthesize lastUpdatedItem;
@synthesize updateTimer;
@synthesize twitterPINField;
@synthesize twitterCheckPINButton;
@synthesize twitterTargetField;
@synthesize requestIndicator;
@synthesize requestButton;
@synthesize checkPINIndicator;
@synthesize twitterStatusSuccessIcon;
@synthesize twitterStatusFailIcon;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Init the operation queue that is used to run the update operations
    updateQueue = [[NSOperationQueue alloc] init];

    // Create the status bar item (= icon near clock)
    NSStatusBar *bar = [NSStatusBar systemStatusBar];
    statusItem = [bar statusItemWithLength:NSVariableStatusItemLength];
    
    // Load and set the status bar item icon from app bundle
    NSString *iconPath = [[NSBundle mainBundle] pathForResource:@"defaultMenuIcon" ofType:@"png"];
    NSImage *statusIcon = [[NSImage alloc] initWithContentsOfFile:iconPath];
    [statusIcon setSize:NSMakeSize(25.0, 15.0)];
    [statusItem setImage:statusIcon];
    [statusItem setHighlightMode:YES];
    [statusItem setMenu:statusMenu];
    
    // Use the same iconPath string variable to load the alternate image
    iconPath = [[NSBundle mainBundle] pathForResource:@"selectedMenuIcon" ofType:@"png"];
    NSImage *alternateIcon = [[NSImage alloc] initWithContentsOfFile:iconPath];
    [alternateIcon setSize:NSMakeSize(25.0, 15.0)];
    [statusItem setAlternateImage:alternateIcon];
    
    separator = [NSMenuItem separatorItem];         // Create the separator item that appears
                                                    // between the 'Last updated:' label and links
    
    updatedLabel = [[NSMenuItem alloc] init];       // Create the label that shows when
    [updatedLabel setTitle:@"Updated Pages:"];      // the sites were last checked
    [updatedLabel setEnabled:NO];
    
    // Get the twitter authentication and if it's valid, show the checkmark icon
    // in the preferences
    accessToken = [[OAToken alloc] initWithKeychainUsingAppName:@"iChangeChecker"
                                            serviceProviderName:@"twitter.com"];
    if (accessToken) {
        [twitterStatusSuccessIcon setHidden:NO];
    }
    
    // Load the web page list from the database
    [siteArrayController fetchWithRequest:nil merge:YES error:NULL];
    NSArray *updatedPages = [siteArrayController arrangedObjects];
    for (int i = 0; i < [updatedPages count]; i++) {
        if ([[updatedPages[i] valueForKey:@"updatedStatus"] isEqualToNumber:@NO]) {
            continue;
        }
        if ([statusMenu indexOfItem:updatedLabel] == -1) {
            [statusMenu insertItem:updatedLabel atIndex:[statusMenu indexOfItemWithTag:5]];
            [statusMenu insertItem:separator atIndex:[statusMenu indexOfItemWithTag:5]];
            NSString *iconPath = [[NSBundle mainBundle] pathForResource:@"updatedMenuIcon" ofType:@"png"];
            NSImage *statusIcon = [[NSImage alloc] initWithContentsOfFile:iconPath];
            [statusIcon setSize:NSMakeSize(25.0, 15.0)];
            [statusItem setImage:statusIcon];
        }
        
        if ([updatedPages[i] valueForKey:@"name"] == nil) {
            [updatedPages[i] setValue:[updatedPages[i] valueForKey:@"address"] forKey:@"name"];
        }
        
        UpdatedPageMenuItem *item = [[UpdatedPageMenuItem alloc] initWithTitle:[updatedPages[i] valueForKey:@"name"] action:@selector(openPage:) keyEquivalent:@""];
        [item setAddress:[updatedPages[i] valueForKey:@"address"]];
        [item setID:[updatedPages[i] valueForKey:@"ID"]];
        [item setDelegate:self];
        [item setTarget:self];
        [statusMenu insertItem:item atIndex:[statusMenu indexOfItem:separator]];
    }
    
    NSUserDefaultsController *udController = [NSUserDefaultsController sharedUserDefaultsController];
    [udController addObserver:self 
                   forKeyPath:@"values.updateInterval" 
                      options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) 
                      context:NULL];
    
    //If update interval is set to just manual, don't update or set timer
    if ([[[NSUserDefaultsController sharedUserDefaultsController] valueForKeyPath:@"values.updateInterval"] intValue] != 6) {
        [self updateAll:self];
        
        NSInteger tag = [[[NSUserDefaultsController sharedUserDefaultsController] 
                          valueForKeyPath:@"values.updateInterval"] intValue];
        NSTimeInterval interval = 0.0;
        switch (tag) {
            case 0:
                interval = (5.0 * 60.0);
                break;
            case 1:
                interval = (10.0 * 60.0);
                break;
            case 2:
                interval = (30.0 * 60.0);
                break;
            case 3:
                interval = (60.0 * 60.0);
                break;
            case 4:
                interval = (24.0 * 60.0 * 60.0);
                break;
            default:
                break;
        }
        //Set update timer running
        NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(updateTimerFired:) userInfo:nil repeats:YES];
        self.updateTimer = timer;
    }
    [self chooseGeneralTab:self];
    [toolbar setSelectedItemIdentifier:@"general"];
}
/**
Returns the directory the application  uses to store the Core Data store file. This code uses a directory named "iChangeChecker" in the user's Library directory.
 */
- (NSURL *)applicationFilesDirectory {

    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSURL *libraryURL = [[fileManager URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask] lastObject];
    return [libraryURL URLByAppendingPathComponent:@"iChangeChecker"];
}

/**
    Creates if necessary and returns the managed object model for the application.
 */
- (NSManagedObjectModel *)managedObjectModel {
    if (__managedObjectModel) {
        return __managedObjectModel;
    }
	
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"iChangeChecker" withExtension:@"momd"];
    __managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];    
    return __managedObjectModel;
}

/**
    Returns the persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. (The directory for the store is created, if necessary.)
 */
- (NSPersistentStoreCoordinator *) persistentStoreCoordinator {
    if (__persistentStoreCoordinator) {
        return __persistentStoreCoordinator;
    }

    NSManagedObjectModel *mom = [self managedObjectModel];
    if (!mom) {
        NSLog(@"%@:%@ No model to generate a store from", [self class], NSStringFromSelector(_cmd));
        return nil;
    }

    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSURL *applicationFilesDirectory = [self applicationFilesDirectory];
    NSError *error = nil;
    
    NSDictionary *properties = [applicationFilesDirectory resourceValuesForKeys:@[NSURLIsDirectoryKey] error:&error];
        
    if (!properties) {
        BOOL ok = NO;
        if ([error code] == NSFileReadNoSuchFileError) {
            ok = [fileManager createDirectoryAtPath:[applicationFilesDirectory path] withIntermediateDirectories:YES attributes:nil error:&error];
        }
        if (!ok) {
            [[NSApplication sharedApplication] presentError:error];
            return nil;
        }
    }
    else {
        if ([properties[NSURLIsDirectoryKey] boolValue] != YES) {
            // Customize and localize this error.
            NSString *failureDescription = [NSString stringWithFormat:@"Expected a folder to store application data, found a file (%@).", [applicationFilesDirectory path]]; 
            
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            [dict setValue:failureDescription forKey:NSLocalizedDescriptionKey];
            error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:101 userInfo:dict];
            
            [[NSApplication sharedApplication] presentError:error];
            return nil;
        }
    }
    
    NSURL *url = [applicationFilesDirectory URLByAppendingPathComponent:@"iChangeChecker.storedata"];
    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:url options:nil error:&error]) {
        [[NSApplication sharedApplication] presentError:error];
        return nil;
    }

    return __persistentStoreCoordinator;
}

/**
    Returns the managed object context for the application (which is already
    bound to the persistent store coordinator for the application.) 
 */
- (NSManagedObjectContext *) managedObjectContext {
    if (__managedObjectContext) {
        return __managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (!coordinator) {
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        [dict setValue:@"Failed to initialize the store" forKey:NSLocalizedDescriptionKey];
        [dict setValue:@"There was an error building up the data file." forKey:NSLocalizedFailureReasonErrorKey];
        NSError *error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
        [[NSApplication sharedApplication] presentError:error];
        return nil;
    }
    __managedObjectContext = [[NSManagedObjectContext alloc] init];
    [__managedObjectContext setPersistentStoreCoordinator:coordinator];

    return __managedObjectContext;
}

/**
    Returns the NSUndoManager for the application. In this case, the manager returned is that of the managed object context for the application.
 */
- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window {
    return [[self managedObjectContext] undoManager];
}

/**
    Performs the save action for the application, which is to send the save: message to the application's managed object context. Any encountered errors are presented to the user.
 */
- (IBAction) saveAction:(id)sender {
    NSError *error = nil;
    
    if (![[self managedObjectContext] commitEditing]) {
        NSLog(@"%@:%@ unable to commit editing before saving", [self class], NSStringFromSelector(_cmd));
    }

    if (![[self managedObjectContext] save:&error]) {
        [[NSApplication sharedApplication] presentError:error];
    }
}

- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {

    // Save changes in the application's managed object context before the application terminates.

    if (!__managedObjectContext) {
        return NSTerminateNow;
    }

    if (![[self managedObjectContext] commitEditing]) {
        NSLog(@"%@:%@ unable to commit editing to terminate", [self class], NSStringFromSelector(_cmd));
        return NSTerminateCancel;
    }

    if (![[self managedObjectContext] hasChanges]) {
        return NSTerminateNow;
    }

    NSError *error = nil;
    if (![[self managedObjectContext] save:&error]) {

        // Customize this code block to include application-specific recovery steps.              
        BOOL result = [sender presentError:error];
        if (result) {
            return NSTerminateCancel;
        }

        NSString *question = NSLocalizedString(@"Could not save changes while quitting. Quit anyway?", @"Quit without saves error question message");
        NSString *info = NSLocalizedString(@"Quitting now will lose any changes you have made since the last successful save", @"Quit without saves error question info");
        NSString *quitButton = NSLocalizedString(@"Quit anyway", @"Quit anyway button title");
        NSString *cancelButton = NSLocalizedString(@"Cancel", @"Cancel button title");
        NSAlert *alert = [[NSAlert alloc] init];
        [alert setMessageText:question];
        [alert setInformativeText:info];
        [alert addButtonWithTitle:quitButton];
        [alert addButtonWithTitle:cancelButton];

        NSInteger answer = [alert runModal];
        alert = nil;
        
        if (answer == NSAlertAlternateReturn) {
            return NSTerminateCancel;
        }
    }

    return NSTerminateNow;
}


- (IBAction)updateAll:(id)sender {
    if ([updateQueue operationCount] == 0) {
        updateOperation = [[UpdateOperation alloc] initWithDelegate:self];
        [updateQueue addOperation:updateOperation];
    }
}

- (IBAction)openPage:(id)sender {
    [sender openPage];
    [statusMenu removeItem:sender];
    for (int i = 0; i < [[siteArrayController arrangedObjects] count]; i++) {
        NSString *compID = [[siteArrayController arrangedObjects][i] valueForKey:@"ID"];
        if ([compID isEqualToString:[(UpdatedPageMenuItem*)sender ID]]) {
            [[siteArrayController arrangedObjects][i] setValue:@NO forKey:@"updatedStatus"];
            [self.managedObjectContext save:NULL];
            break;
        }
    }
    if ([statusMenu indexOfItem:separator] == 4) {
        [statusMenu removeItem:separator];
        [statusMenu removeItem:updatedLabel];
        NSString *iconPath = [[NSBundle mainBundle] pathForResource:@"defaultMenuIcon" ofType:@"png"];
        NSImage *statusIcon = [[NSImage alloc] initWithContentsOfFile:iconPath];
        [statusIcon setSize:NSMakeSize(25.0, 15.0)];
        [statusItem setImage:statusIcon];
    }
}

- (IBAction)showPreferences:(id)sender {
    [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
    [window makeKeyAndOrderFront:self];
}

- (void)updateTimerFired:(NSTimer*)timer {
    [self updateAll:self];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object 
                        change:(NSDictionary *)change context:(void *)context {
    if (![keyPath isEqualToString:@"values.updateInterval"]) {
        return;
    }
    if (updateTimer != nil) {
        [updateTimer invalidate];
        updateTimer = nil;
    }
    
    if ([[[NSUserDefaultsController sharedUserDefaultsController] 
          valueForKeyPath:@"values.updateInterval"] intValue] != 6) {
        NSInteger tag = [[[NSUserDefaultsController sharedUserDefaultsController] 
                          valueForKeyPath:@"values.updateInterval"] intValue];
        NSTimeInterval interval = 0.0;
        switch (tag) {
            case 0:
                interval = (5.0 * 60.0);
                break;
            case 1:
                interval = (10.0 * 60.0);
                break;
            case 2:
                interval = (30.0 * 60.0);
                break;
            case 3:
                interval = (60.0 * 60.0);
                break;
            case 4:
                interval = (24.0 * 60.0 * 60.0);
                break;
            default:
                break;
        }
        //Set update timer running
        NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(updateTimerFired:) userInfo:nil repeats:YES];
        self.updateTimer = timer;
    }
}

- (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)bar {
    return [bar items];
}

- (void)sendTwitterNotificationWithName:(NSString*)name link:(NSString*)link {
    if (!accessToken || [[twitterTargetField stringValue] isEqualToString:@""])
        return;
    
    NSString *twitterMessage = [NSString stringWithFormat:@"Page %@ has been updated. Link: %@", name, link, nil];
    if ([twitterMessage length] > 140) {
        twitterMessage = [NSString stringWithFormat:@"Page %@ has been updated.", name, nil];
        if ([twitterMessage length] > 140) return;
    }
    
    OAConsumer *consumer = [[OAConsumer alloc] initWithKey:@"WPl2lfFu8rWa4lCPsOpnw"
													secret:@"FGHwdl1K2OFkNWuOBDfTND4ec81UKGAlbrKiPJGGFg"];
	
	OADataFetcher *fetcher = [[OADataFetcher alloc] init];

	NSURL *url = [NSURL URLWithString:@"https://api.twitter.com/1/direct_messages/new.xml"];
	
	OAMutableURLRequest *request = [[OAMutableURLRequest alloc] initWithURL:url
																   consumer:consumer
																	  token:accessToken
																	  realm:nil
														  signatureProvider:nil];
    
    [request setHTTPMethod:@"POST"];
    OARequestParameter *userParam = [[OARequestParameter alloc] initWithName:@"screen_name" value:[twitterTargetField stringValue]];
    OARequestParameter *textParam = [[OARequestParameter alloc] initWithName:@"text" value:twitterMessage];
    
    NSArray *paramArray = @[textParam, userParam];
    [request setParameters:paramArray];
    
	NSLog(@"Sending DM");
	
	[fetcher fetchDataWithRequest:request 
						 delegate:self
				didFinishSelector:@selector(apiTicket:didFinishWithData:)
				  didFailSelector:@selector(apiTicket:didFailWithError:)];
}

- (IBAction) getRequestToken:(id)sender
{
    [requestButton setHidden:YES];
    [requestIndicator startAnimation:self];
    [preferencesGeneralView display];
    
	OAConsumer *consumer = [[OAConsumer alloc] initWithKey:@"WPl2lfFu8rWa4lCPsOpnw"
													secret:@"FGHwdl1K2OFkNWuOBDfTND4ec81UKGAlbrKiPJGGFg"];
	
	OADataFetcher *fetcher = [[OADataFetcher alloc] init];
	
	NSURL *url = [NSURL URLWithString:@"https://api.twitter.com/oauth/request_token"];
	
	OAMutableURLRequest *request = [[OAMutableURLRequest alloc] initWithURL:url
																   consumer:consumer
																	  token:nil
																	  realm:nil
														  signatureProvider:nil];
    
	[request setHTTPMethod:@"POST"];
	
	NSLog(@"Getting request token...");
	
	[fetcher fetchDataWithRequest:request 
						 delegate:self
				didFinishSelector:@selector(requestTokenTicket:didFinishWithData:)
				  didFailSelector:@selector(requestTokenTicket:didFailWithError:)];
}

- (IBAction) getAccessToken:(id)sender
{
    [twitterCheckPINButton setHidden:YES];
    [checkPINIndicator startAnimation:self];
    [preferencesGeneralView display];
    
	OAConsumer *consumer = [[OAConsumer alloc] initWithKey:@"WPl2lfFu8rWa4lCPsOpnw"
													secret:@"FGHwdl1K2OFkNWuOBDfTND4ec81UKGAlbrKiPJGGFg"];
	
	OADataFetcher *fetcher = [[OADataFetcher alloc] init];
	
	NSURL *url = [NSURL URLWithString:@"https://api.twitter.com/oauth/access_token"];
	
	[accessToken setVerifier:[self.twitterPINField stringValue]];
	NSLog(@"Using PIN %@", accessToken.verifier);
	
	OAMutableURLRequest *request = [[OAMutableURLRequest alloc] initWithURL:url
																   consumer:consumer
																	  token:accessToken
																	  realm:nil
														  signatureProvider:nil];
	[request setHTTPMethod:@"POST"];
	NSLog(@"Getting access token...");
	
	[fetcher fetchDataWithRequest:request 
						 delegate:self
				didFinishSelector:@selector(accessTokenTicket:didFinishWithData:)
				  didFailSelector:@selector(accessTokenTicket:didFailWithError:)];
}


- (IBAction)chooseGeneralTab:(id)sender {
    if (![selectedPrefTab isEqualToString:kGeneralTab]) {
        [[preferencesGeneralView animator] setAlphaValue:0.0];
        [[preferencesPagesView animator] setAlphaValue:0.0];
        [mainView addSubview:preferencesGeneralView];
        [[preferencesGeneralView animator] setAlphaValue:1.0];
        [preferencesPagesView removeFromSuperview];
        
        selectedPrefTab = kGeneralTab;
    }
}

- (IBAction)choosePagesTab:(id)sender {
    if (![selectedPrefTab isEqualToString:kPagesTab]) {
        [[preferencesPagesView animator] setAlphaValue:0.0];
        [[preferencesGeneralView animator] setAlphaValue:0.0];
        [mainView addSubview:preferencesPagesView];
        [[preferencesPagesView animator] setAlphaValue:1.0];
        [preferencesGeneralView removeFromSuperview];
        
        selectedPrefTab = kPagesTab;
    }
}

- (void) requestTokenTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data
{
	if (ticket.didSucceed)
	{
		NSString *responseBody = [[NSString alloc] initWithData:data 
													   encoding:NSUTF8StringEncoding];
		accessToken = [[OAToken alloc] initWithHTTPResponseBody:responseBody];
        
		NSLog(@"Got request token. Redirecting to twitter auth page...");
		
		NSString *address = [NSString stringWithFormat:
							 @"https://api.twitter.com/oauth/authorize?oauth_token=%@",
							 accessToken.key];
		
		NSURL *url = [NSURL URLWithString:address];
		[[NSWorkspace sharedWorkspace] openURL:url];
        [requestIndicator stopAnimation:self];
        [requestButton setHidden:NO];
        
	}
}

- (void) requestTokenTicket:(OAServiceTicket *)ticket didFailWithError:(NSError *)error
{
	NSLog(@"Getting request token failed: %@", [error localizedDescription]);
    [requestIndicator stopAnimation:self];
    [requestButton setHidden:NO];
}

- (void) accessTokenTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data
{
	if (ticket.didSucceed)
	{
		NSString *responseBody = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
		accessToken = [[OAToken alloc] initWithHTTPResponseBody:responseBody];
		[accessToken storeInDefaultKeychainWithAppName:@"iChangeChecker" serviceProviderName:@"twitter.com"];
        
		NSLog(@"Got access token. Ready to use Twitter API.");
        [checkPINIndicator stopAnimation:self];
        [twitterCheckPINButton setHidden:NO];
        [twitterStatusFailIcon setHidden:YES];
        [twitterStatusSuccessIcon setHidden:NO];
        [preferencesGeneralView display];
	}
}

- (void) accessTokenTicket:(OAServiceTicket *)ticket didFailWithError:(NSError *)error
{
    [checkPINIndicator stopAnimation:self];
    [twitterCheckPINButton setHidden:NO];
    [twitterStatusFailIcon setHidden:NO];
    [twitterStatusSuccessIcon setHidden:YES];
	NSLog(@"Getting access token failed: %@", [error localizedDescription]);
}

- (void) apiTicket:(OAServiceTicket *)ticket didFinishWithData:(NSData *)data
{
	// Method does nothing, but left here for debug
    /*
    if (ticket.didSucceed)
	{
		NSString *responseBody = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        
	}
     */
}

- (void) apiTicket:(OAServiceTicket *)ticket didFailWithError:(NSError *)error
{
	NSLog(@"Sending DM failed: %@", [error localizedDescription]);
}


@end
